﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using Tesla.Graphics;

namespace Tesla.Content.Loaders {
    /// <summary>
    /// Resource loader for single-material TEM files, or the first material in a multi-material TEM file.
    /// </summary>
    public sealed class MaterialLoader : ResourceLoader<Material> {
        private MaterialParser _parser;

        /// <summary>
        /// Initializes a new instance of the <see cref="MaterialCollectionLoader"/> class.
        /// </summary>
        public MaterialLoader() : base(".tem") { }

        /// <summary>
        /// Called when the loader is initialized, usually when it's added to a content manager.
        /// </summary>
        protected override void OnInitialize() {
            _parser = new MaterialParser(ContentManager);
        }

        /// <summary>
        /// Loads the specified resource.
        /// </summary>
        /// <param name="resource">The resource to load.</param>
        /// <param name="parameters">Loader parameters.</param>
        /// <returns>The loaded material</returns>
        public override Material Load(IResource resource, LoaderParameters parameters) {
            if(_parser == null) {
                _parser = new MaterialParser(ContentManager);
            }

            if(parameters is MaterialLoaderParameters) {
                return _parser.LoadMaterial(resource, parameters);
            } else {
                return _parser.LoadFirstMaterial(resource, parameters);
            }
        }
    }

    /*
    public class MaterialLoader : ResourceLoader<Material> {
        private TokenReader _reader;
        //Cache to let us re-use render stateNames between instances
        private Dictionary<RenderStateKey, RenderState>[] _cachedRenderStates;

        //Instance-specific data
        private Dictionary<String, MaterialParameter> _matParams;
        private Dictionary<String, MaterialEngineParameter> _engineParams;
        private Dictionary<String, RenderState> _renderStates;
        private Dictionary<String, MaterialTechnique> _techniques;

        public MaterialLoader()
            : base(typeof(Material), ".tem") {
            _reader = new TokenReader(new char[] { ' ', ';' });
            _cachedRenderStates = new Dictionary<RenderStateKey, RenderState>[] {
                    new Dictionary<RenderStateKey, RenderState>(),
                    new Dictionary<RenderStateKey, RenderState>(),
                    new Dictionary<RenderStateKey, RenderState>(),
                    new Dictionary<RenderStateKey, RenderState>()
                };
            _renderStates = new Dictionary<String, RenderState>();
            _matParams = new Dictionary<String, MaterialParameter>();
            _engineParams = new Dictionary<String, MaterialEngineParameter>();
            _techniques = new Dictionary<String, MaterialTechnique>();
        }

        private void ClearTransientData() {
            _renderStates.Clear();
            _matParams.Clear();
            _engineParams.Clear();
            _techniques.Clear();
        }

        public override Material Load(IResource resource, LoaderParameters parameters) {
            _reader.Read(resource.OpenStream());
            Material material = null;
            try {
                material = ParseMaterial();
            } catch(Exception e) {
                //If we hit a parse exception, ensure we close out and clear the loader for future use.
                _reader.Close();
                ClearTransientData();
                //Throw a new exception detailing the parse error
                throw new TeslaException(e.Message, e);
            }

            _reader.Close();

            //Now set all the data
            SetParameters(material);
            SetEngineParameters(material);
            SetRenderStates(material);

            //Clear data
            ClearTransientData();
            return material;
        }

        private void SetRenderStates(Material material) {
            foreach(KeyValuePair<String, MaterialTechnique> kv in _techniques) {
                MaterialTechnique tech = kv.Value;
                ValidateTechnique(ref tech, material);
                List<MaterialPass> passes = tech.Passes;
                for(int i = 0; i < passes.Count; i++) {
                    MaterialPass pass = passes[i];
                    if(pass.BlendStateName != null) {
                        RenderState bs;
                        if(_renderStates.TryGetValue(pass.BlendStateName, out bs)) {
                            if(!(bs is BlendState)) {
                                ThrowException(String.Format("Type mismatch, identifier is not a BlendState in Pass {0}, Technique {1}", pass.Name, tech.Name));
                            } else {
                                material.SetRenderState(bs, tech.Name, pass.Name);
                            }
                        } else {
                            ThrowException(String.Format("BlendState identifier {0} is not defined.", pass.BlendStateName));
                        }
                    }

                    if(pass.DepthStencilStateName != null) {
                        RenderState dss;
                        if(_renderStates.TryGetValue(pass.DepthStencilStateName, out dss)) {
                            if(!(dss is DepthStencilState)) {
                                ThrowException(String.Format("Type mismatch, identifier is not a DepthStencilState in Pass {0}, Technique {1}", pass.Name, tech.Name));
                            } else {
                                material.SetRenderState(dss, tech.Name, pass.Name);
                            }
                        } else {
                            ThrowException(String.Format("DepthStencilState identifier {0} is not defined.", pass.BlendStateName));
                        }
                    }

                    if(pass.RasterizerStateName != null) {
                        RenderState rs;
                        if(_renderStates.TryGetValue(pass.RasterizerStateName, out rs)) {
                            if(!(rs is RasterizerState)) {
                                ThrowException(String.Format("Type mismatch, identifier is not a RasterizerState in Pass {0}, Technique {1}", pass.Name, tech.Name));
                            } else {
                                material.SetRenderState(rs, tech.Name, pass.Name);
                            }
                        } else {
                            ThrowException(String.Format("RasterizerState identifier {0} is not defined.", pass.BlendStateName));
                        }
                    }
                }
            }
        }

        private void ValidateTechnique(ref MaterialTechnique tech, Material material) {
            Effect e = material.Effect;
            String techName = tech.Name;
            int passCount = tech.Passes.Count;

            IEffectTechnique eTech = e.Techniques[techName];
            if(eTech == null) {
                ThrowException(String.Format("Technique named {0} does not exist in the effect.", techName));
            }

            if(passCount > eTech.Passes.Count) {
                ThrowException(String.Format("Technique named {0} has more passes than actually defined in the effect.", techName));
            }

            foreach(MaterialPass pass in tech.Passes) {
                IEffectPass ePass = eTech.Passes[pass.Name];
                if(ePass == null) {
                    ThrowException(String.Format("Pass {0} in Technique {1} does not exist in effect.", pass.Name, tech.Name));
                }
            }

            //Now go through and set the states for all passes.
            for(int i = 0; i < material.PassCount; i++) {
                if(tech.BlendStateName != null) {
                    RenderState bs;
                    if(_renderStates.TryGetValue(tech.BlendStateName, out bs)) {
                        if(!(bs is BlendState)) {
                            ThrowException(String.Format("Type mismatch, identifier is not a BlendState in Technique {0}", tech.Name));
                        } else {
                            material.SetRenderState(bs, tech.Name, i);
                        }
                    } else {
                        ThrowException(String.Format("BlendState identifier {0} is not defined.", tech.BlendStateName));
                    }
                }

                if(tech.DepthStencilStateName != null) {
                    RenderState dss;
                    if(_renderStates.TryGetValue(tech.DepthStencilStateName, out dss)) {
                        if(!(dss is DepthStencilState)) {
                            ThrowException(String.Format("Type mismatch, identifier is not a DepthStencilState in Technique {0}", tech.Name));
                        } else {
                            material.SetRenderState(dss, tech.Name, i);
                        }
                    } else {
                        ThrowException(String.Format("DepthStencilState identifier {0} is not defined.", tech.BlendStateName));
                    }
                }

                if(tech.RasterizerStateName != null) {
                    RenderState rs;
                    if(_renderStates.TryGetValue(tech.RasterizerStateName, out rs)) {
                        if(!(rs is RasterizerState)) {
                            ThrowException(String.Format("Type mismatch, identifier is not a RasterizerState in Technique {0}", tech.Name));
                        } else {
                            material.SetRenderState(rs, tech.Name, i);
                        }
                    } else {
                        ThrowException(String.Format("RasterizerState identifier {0} is not defined.", tech.BlendStateName));
                    }
                }

            }
        }

        private void SetParameters(Material material) {
            foreach(KeyValuePair<String, MaterialParameter> kv in _matParams) {
                MaterialParameter param = kv.Value;
                switch(param.ParameterType) {
                    case MaterialParameterType.Bool:
                        material.SetParameter(param.Name, param.IsSemantic, param.BoolValue);
                        break;
                    case MaterialParameterType.Int32:
                        material.SetParameter(param.Name, param.IsSemantic, param.IntValue);
                        break;
                    case MaterialParameterType.Matrix:
                        material.SetParameter(param.Name, param.IsSemantic, param.MatrixValue);
                        break;
                    case MaterialParameterType.Single:
                        material.SetParameter(param.Name, param.IsSemantic, param.SingleValue);
                        break;
                    case MaterialParameterType.Texture:
                        material.SetParameter(param.Name, param.IsSemantic, param.TextureValue);
                        break;
                    case MaterialParameterType.Vector2:
                        material.SetParameter(param.Name, param.IsSemantic, param.Vector2Value);
                        break;
                    case MaterialParameterType.Vector3:
                        material.SetParameter(param.Name, param.IsSemantic, param.Vector3Value);
                        break;
                    case MaterialParameterType.Vector4:
                        material.SetParameter(param.Name, param.IsSemantic, param.Vector4Value);
                        break;
                }
            }
        }

        private void SetEngineParameters(Material material) {
            foreach(KeyValuePair<String, MaterialEngineParameter> kv in _engineParams) {
                MaterialEngineParameter param = kv.Value;
                material.SetEngineParameter(param.Name, param.IsSemantic, param.EngineValue);
            }
        }

        private Material ParseParentMaterial(Stream stream, String name) {
            TokenReader childReader = _reader;
            _reader = new TokenReader(new char[] { ' ', ';' });
            _reader.Read(stream);
            Material material = null;
            if(_reader.HasNext("Material")) {
                _reader.NextToken();

                if(_reader.HasNext("{") || _reader.HasNext(":")) {
                    ThrowException("Missing material name.");
                }

                //Old material name
                _reader.NextToken();

                //If this material has a parent, throw an error. Inheritance can only be a depth of one
                if(_reader.HasNext(":")) {
                    ThrowException("Cannot have more than one parent");
                } else if(_reader.HasNext("{")) {
                    material = new Material(name);
                    _reader.NextToken();
                } else {
                    ThrowException("Parse error, missing syntax.");
                }

                //Parse the material file
                while(_reader.HasNext()) {
                    ParseSubBlock(material);
                    if(_reader.HasNext("}")) {
                        break;
                    }
                }
            } else {
                ThrowException("Material keyword missing.");
            }

            _reader.Close();
            _reader = childReader;
            return material;
        }

        private Material ParseMaterial() {
            Material material = null;
            if(_reader.HasNext("Material")) {
                _reader.NextToken();

                if(_reader.HasNext("{") || _reader.HasNext(":")) {
                    ThrowException("Missing material name.");
                }

                String name = _reader.NextToken();

                //Check if we inherit from a parent material file - if we do, we must try to open
                //it and parse it, then parse this file. If not, just create a new material and parse this
                //file
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    if(_reader.HasNext("{")) {
                        ThrowException("Missing parent material file name.");
                    }
                    String file = _reader.NextToken();
                    material = ParseParentMaterial(ContentManager.OpenStream(file), name);
                    if(_reader.HasNext("{")) {
                        _reader.NextToken();
                    } else {
                        ThrowException("Missing { brace in material block");
                    }
                } else if(_reader.HasNext("{")) {
                    material = new Material(name);
                    _reader.NextToken();
                } else {
                    ThrowException("Parse error, missing syntax.");
                }

                //Parse the material file
                while(_reader.HasNext()) {
                    ParseSubBlock(material);
                    if(_reader.HasNext("}")) {
                        break;
                    }
                }
            } else {
                ThrowException("Material keyword missing.");
            }

            return material;
        }

        private void ParseSubBlock(Material material) {
            String block = _reader.NextToken();
            if(block.Equals("Effect")) {
                ParseEffect(material);
            } else if(block.Equals("MaterialParameters")) {
                ParseMaterialParameters(material);
            } else if(block.Equals("EngineParameters")) {
                ParseEngineParameters(material);
            } else if(block.Equals("RenderStates")) {
                ParseRenderStates(material);
            } else if(block.Equals("Technique")) {
                ParseTechnique(material);
            }
        }

        private void ParseEffect(Material material) {
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                String file = String.Empty;
                String technique = String.Empty;
                for(int i = 0; i < 2; i++) {
                    if(_reader.HasNext("File")) {
                        _reader.NextToken();
                        if(_reader.HasNext(":")) {
                            _reader.NextToken();
                            file = _reader.NextToken();
                        } else {
                            ThrowException("Missing : syntax");
                        }
                    } else if(_reader.HasNext("Technique")) {
                        _reader.NextToken();
                        if(_reader.HasNext(":")) {
                            _reader.NextToken();
                            technique = _reader.NextToken();
                        } else {
                            ThrowException("Missing : syntax");
                        }
                    } else {
                        ThrowException("Parse error, invalid syntax");
                    }
                }
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                    material.LoadEffect(ContentManager.Load<Effect>(file));
                    if(!material.IsValid) {
                        ThrowException("Could not load effect.");
                    }
                    material.SetActiveTechnique(technique);
                    return;
                } else {
                    ThrowException("Missing closing } bracket in effect block");
                }
            } else {
                ThrowException("Missing { brace in effect block.");
            }
        }

        private void ParseMaterialParameters(Material material) {
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                while(ParseParameter(material)) {
                    continue;
                }
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace in material parameter block");
                }
            } else {
                ThrowException("Missing { brace in material parameter block");
            }
        }

        private bool ParseParameter(Material material) {
            if(_reader.HasNext("float")) {
                _reader.NextToken();
                ParseSingle(material);
                return true;
            } else if(_reader.HasNext("int")) {
                _reader.NextToken();
                ParseInt(material);
                return true;
            } else if(_reader.HasNext("bool")) {
                _reader.NextToken();
                ParseBool(material);
                return true;
            } else if(_reader.HasNext("Vector2")) {
                _reader.NextToken();
                ParseVector2(material);
                return true;
            } else if(_reader.HasNext("Vector3")) {
                _reader.NextToken();
                ParseVector3(material);
                return true;
            } else if(_reader.HasNext("Vector4")) {
                _reader.NextToken();
                ParseVector4(material);
                return true;
            } else if(_reader.HasNext("Quaternion")) {
                _reader.NextToken();
                ParseQuaternion(material);
                return true;
            } else if(_reader.HasNext("Matrix")) {
                _reader.NextToken();
                ParseMatrix(material);
                return true;
            } else if(_reader.HasNext("Texture2D")) {
                _reader.NextToken();
                ParseTexture2D(material);
                return true;
            } else if(_reader.HasNext("TextureCube")) {
                _reader.NextToken();
                ParseTextureCube(material);
                return true;
            } else if(_reader.HasNext("Texture1D")) {
                _reader.NextToken();
                ParseTexture1D(material);
                return true;
            } else if(_reader.HasNext("Texture3D")) {
                _reader.NextToken();
                ParseTexture3D(material);
                return true;
            }
            return false;
        }


        private void ParseSingle(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                MaterialParameter param = new MaterialParameter(null, name, false, _reader.NextFloat());
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseInt(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                MaterialParameter param = new MaterialParameter(null, name, false, _reader.NextInt());
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseBool(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                MaterialParameter param = new MaterialParameter(null, name, false, _reader.NextBool());
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseVector2(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                Vector2 v = new Vector2(_reader.NextFloat(), _reader.NextFloat());
                MaterialParameter param = new MaterialParameter(null, name, false, v);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseVector3(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                Vector3 v = new Vector3(_reader.NextFloat(), _reader.NextFloat(), _reader.NextFloat());
                MaterialParameter param = new MaterialParameter(null, name, false, v);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseVector4(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                Vector4 v = new Vector4(_reader.NextFloat(), _reader.NextFloat(), _reader.NextFloat(), _reader.NextFloat());
                MaterialParameter param = new MaterialParameter(null, name, false, v);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseQuaternion(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                Quaternion q = new Quaternion(_reader.NextFloat(), _reader.NextFloat(), _reader.NextFloat(), _reader.NextFloat());
                MaterialParameter param = new MaterialParameter(null, name, false, new Vector4(q.X, q.Y, q.Z, q.W));
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseMatrix(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                Matrix m = new Matrix();
                for(int i = 0; i < 4; i++) {
                    switch(i) {
                        case 0:
                            m.M11 = _reader.NextFloat();
                            m.M12 = _reader.NextFloat();
                            m.M13 = _reader.NextFloat();
                            m.M14 = _reader.NextFloat();
                            break;
                        case 1:
                            m.M21 = _reader.NextFloat();
                            m.M22 = _reader.NextFloat();
                            m.M23 = _reader.NextFloat();
                            m.M24 = _reader.NextFloat();
                            break;
                        case 2:
                            m.M31 = _reader.NextFloat();
                            m.M32 = _reader.NextFloat();
                            m.M33 = _reader.NextFloat();
                            m.M34 = _reader.NextFloat();
                            break;
                        case 3:
                            m.M41 = _reader.NextFloat();
                            m.M42 = _reader.NextFloat();
                            m.M43 = _reader.NextFloat();
                            m.M44 = _reader.NextFloat();
                            break;
                    }
                }
                MaterialParameter param = new MaterialParameter(null, name, false, m);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseTexture1D(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                String filePath = _reader.NextToken();
                Texture1D texture = ContentManager.Load<Texture1D>(filePath);
                MaterialParameter param = new MaterialParameter(null, name, false, texture);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseTexture2D(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                String filePath = _reader.NextToken();
                Texture2D texture = ContentManager.Load<Texture2D>(filePath);
                MaterialParameter param = new MaterialParameter(null, name, false, texture);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseTexture3D(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                String filePath = _reader.NextToken();
                Texture3D texture = ContentManager.Load<Texture3D>(filePath);
                MaterialParameter param = new MaterialParameter(null, name, false, texture);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseTextureCube(Material material) {
            String name = _reader.NextToken();
            if(_reader.HasNext(":")) {
                _reader.NextToken();
                String filePath = _reader.NextToken();
                TextureCube texture = ContentManager.Load<TextureCube>(filePath);
                MaterialParameter param = new MaterialParameter(null, name, false, texture);
                _matParams[name] = param;
            } else {
                ThrowException("Missing : syntax in parameter declaration.");
            }
        }

        private void ParseEngineParameters(Material material) {
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                while(ParseEngineParameter(material)) {
                    continue;
                }
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing closing } bracket in engine parameter block.");
                }
            } else {
                ThrowException("Missing { brace in engine parameter block");
            }
        }

        private bool ParseEngineParameter(Material material) {
            if(_reader.HasNext("WorldViewProjection")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.WorldViewProjection);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("WorldMatrix")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.WorldMatrix);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("ViewMatrix")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.ViewMatrix);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("ProjectionMatrix")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.ProjectionMatrix);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("CameraPosition")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.CameraPosition);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("ViewProjectionMatrix")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.ViewProjectionMatrix);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("WorldInverseTranspose")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.WorldInverseTranspose);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("WorldViewMatrix")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.WorldViewMatrix);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("WorldMatrixInverse")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.WorldMatrixInverse);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("ViewMatrixInverse")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.ViewMatrixInverse);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("ProjectionMatrixInverse")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.ProjectionMatrixInverse);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("Viewport")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.Viewport);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("Resolution")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.Resolution);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("FrustumNearFar")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.FrustumNearFar);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("AspectRatio")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.AspectRatio);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("CameraDirection")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.CameraDirection);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("CameraUp")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.CameraUp);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("CameraLeft")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.CameraLeft);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("TimePerFrame")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.TimePerFrame);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("Time")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.Time);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("RandomFloat")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.RandomFloat);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            } else if(_reader.HasNext("RandomInt")) {
                _reader.NextToken();
                if(_reader.NextToken().Equals(":")) {
                    String name = _reader.NextToken();
                    MaterialEngineParameter param = new MaterialEngineParameter(null, name, false, EngineValue.RandomInt);
                    _engineParams[name] = param;
                } else {
                    ThrowException("Missing : syntax");
                }
                return true;
            }
            return false;
        }

        private void ParseRenderStates(Material material) {
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                while(ParseRenderState(material)) {
                    continue;
                }
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace in render state block");
                }
            } else {
                ThrowException("Missing { brace in render state block.");
            }
        }

        private bool ParseRenderState(Material material) {
            if(_reader.HasNext("RasterizerState")) {
                _reader.NextToken();
                ParseRasterizerState(material);
                return true;
            } else if(_reader.HasNext("BlendState")) {
                _reader.NextToken();
                ParseBlendState(material);
                return true;
            } else if(_reader.HasNext("DepthStencilState")) {
                _reader.NextToken();
                ParseDepthStencilState(material);
                return true;
            } else if(_reader.HasNextStartsWith("SamplerState")) {
                String name = _reader.NextToken();
                int index = int.Parse(name.Substring(name.LastIndexOf('e') + 1));
                ParseSamplerState(material, index, false);
                return true;
            } else if(_reader.HasNextStartsWith("VertexSamplerState")) {
                String name = _reader.NextToken();
                int index = int.Parse(name.Substring(name.LastIndexOf('e') + 1));
                ParseSamplerState(material, index, true);
                return true;
            }
            return false;
        }

        private void ParseTechnique(Material material) {
            if(_reader.HasNext("{")) {
                ThrowException("Missing identifier for technique.");
            }

            String name = _reader.NextToken();
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                String[] stateNames = new String[3];
                MaterialTechnique technique = new MaterialTechnique();
                technique.Name = name;
                technique.Passes = new List<MaterialPass>();
                while(ParseTechniqueRenderState(stateNames)) {
                    continue;
                }
                technique.RasterizerStateName = stateNames[0];
                technique.BlendStateName = stateNames[1];
                technique.DepthStencilStateName = stateNames[2];
                while(ParsePass(stateNames, ref technique)) {
                    continue;
                }
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException(String.Format("Missing } brace at the end of technique {0} block.", name));
                }

                _techniques.Add(name, technique);
            } else {
                ThrowException("Parse error, missing { brace in technique block");
            }
        }

        private bool ParseTechniqueRenderState(String[] stateNames) {
            if(_reader.HasNext("RasterizerState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    stateNames[0] = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNext("BlendState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    stateNames[1] = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNext("DepthStencilState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    stateNames[2] = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNextStartsWith("SamplerState")) {
                ThrowException("Cannot set sampler state in technique.");
            } else if(_reader.HasNextStartsWith("VertexSamplerState")) {
                ThrowException("Cannot set sampler state in technique.");
            }
            return false;
        }

        private bool ParsePass(String[] stateNames, ref MaterialTechnique technique) {
            if(_reader.HasNext("Pass")) {
                _reader.NextToken();
                if(_reader.HasNext("{")) {
                    ThrowException("Pass must have a name");
                }
                String name = _reader.NextToken();
                if(_reader.HasNext("{")) {
                    _reader.NextToken();
                    MaterialPass pass = new MaterialPass();
                    pass.Name = name;
                    pass.RasterizerStateName = stateNames[0];
                    pass.BlendStateName = stateNames[1];
                    pass.DepthStencilStateName = stateNames[2];

                    while(ParsePassRenderStates(ref pass)) {
                        continue;
                    }

                    if(_reader.HasNext("}")) {
                        _reader.NextToken();
                    } else {
                        ThrowException("Missing } brace in pass block");
                    }

                    technique.Passes.Add(pass);
                    return true;
                } else {
                    ThrowException("Missing { brace in pass block.");
                }
            }
            return false;
        }

        private bool ParsePassRenderStates(ref MaterialPass pass) {
            if(_reader.HasNext("RasterizerState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    pass.RasterizerStateName = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNext("BlendState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    pass.BlendStateName = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNext("DepthStencilState")) {
                _reader.NextToken();
                if(_reader.HasNext(":")) {
                    _reader.NextToken();
                    pass.DepthStencilStateName = _reader.NextToken();
                    return true;
                } else {
                    ThrowException("Parse error, missing : syntax");
                }
            } else if(_reader.HasNextStartsWith("SamplerState")) {
                ThrowException("Cannot set sampler state in a pass");
            } else if(_reader.HasNextStartsWith("VertexSamplerState")) {
                ThrowException("Cannot set sampler state in a pass.");
            }
            return false;
        }

        private void ParseSamplerState(Material material, int index, bool vertexSampler) {
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                //TODO: Parse contents of sampler block
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace at the end of the sampler state block.");
                }
            } else if(_reader.HasNext(":")) {
                _reader.NextToken();
                String type = _reader.NextToken();
                SamplerState ss = null;
                if(type.Equals("AnisotropicClamp")) {
                    ss = SamplerState.AnisotropicClamp;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                } else if(type.Equals("AnisotropicWrap")) {
                    ss = SamplerState.AnisotropicWrap;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                } else if(type.Equals("LinearClamp")) {
                    ss = SamplerState.LinearClamp;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                } else if(type.Equals("LinearWrap")) {
                    ss = SamplerState.LinearWrap;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                } else if(type.Equals("PointClamp")) {
                    ss = SamplerState.PointClamp;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                } else if(type.Equals("PointWrap")) {
                    ss = SamplerState.PointWrap;
                    if(vertexSampler) {
                        material.SamplerStates[index] = ss;
                    } else {
                        material.VertexSamplerStates[index] = ss;
                    }
                }
            } else {
                ThrowException("Parse error, invalid syntax");
            }
        }

        private void ParseBlendState(Material material) {
            if(_reader.HasNext("{") || _reader.HasNext(":")) {
                ThrowException("Missing identifier for blend state.");
            }

            String name = _reader.NextToken();
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                //TODO: Parse contents of the BS block
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace at the end of the rasterizer state block.");
                }
            } else if(_reader.HasNext(":")) {
                _reader.NextToken();
                String type = _reader.NextToken();
                BlendState bs = null;
                if(type.Equals("AdditiveBlend")) {
                    bs = BlendState.AdditiveBlend;
                    _renderStates[name] = bs;
                } else if(type.Equals("Opaque")) {
                    bs = BlendState.Opaque;
                    _renderStates[name] = bs;
                } else if(type.Equals("AlphaBlendNonPremultiplied")) {
                    bs = BlendState.AlphaBlendNonPremultiplied;
                    _renderStates[name] = bs;
                } else if(type.Equals("AlphaBlendPremultiplied")) {
                    bs = BlendState.AlphaBlendPremultiplied;
                    _renderStates[name] = bs;
                }
            } else {
                ThrowException("Parse error, missing syntax");
            }
        }

        private void ParseDepthStencilState(Material material) {
            if(_reader.HasNext("{") || _reader.HasNext(":")) {
                ThrowException("Missing identifier for depthstencil state.");
            }

            String name = _reader.NextToken();
            if(_reader.HasNext("{")) {
                _reader.NextToken();
                //TODO: Parse contents of the BS block
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace at the end of the depthstencil state block.");
                }
            } else if(_reader.HasNext(":")) {
                _reader.NextToken();
                String type = _reader.NextToken();
                DepthStencilState dss = null;
                if(type.Equals("Default")) {
                    dss = DepthStencilState.Default;
                    _renderStates[name] = dss;
                } else if(type.Equals("DepthWriteOff")) {
                    dss = DepthStencilState.DepthWriteOff;
                    _renderStates[name] = dss;
                } else if(type.Equals("None")) {
                    dss = DepthStencilState.None;
                    _renderStates[name] = dss;
                }
            } else {
                ThrowException("Parse error, missing syntax");
            }
        }

        private void ParseRasterizerState(Material material) {
            if(_reader.HasNext("{") || _reader.HasNext(":")) {
                ThrowException("Missing identifier for rasterizer state.");
            }

            String name = _reader.NextToken();

            if(_reader.HasNext("{")) {
                _reader.NextToken();
                //TODO: Parse the conents of the RS block, create a RS key for it, then try to find it in the cache
                if(_reader.HasNext("}")) {
                    _reader.NextToken();
                } else {
                    ThrowException("Missing } brace at the end of the rasterizer state block.");
                }
            } else if(_reader.HasNext(":")) {
                _reader.NextToken();
                String type = _reader.NextToken();
                RasterizerState rs = null;
                if(type.Equals("CullNone")) {
                    rs = RasterizerState.CullNone;
                    _renderStates[name] = rs;
                } else if(type.Equals("CullNoneWireframe")) {
                    rs = RasterizerState.CullNoneWireframe;
                    _renderStates[name] = rs;
                } else if(type.Equals("CullBackClockwiseFront")) {
                    rs = RasterizerState.CullBackClockwiseFront;
                    _renderStates[name] = rs;
                } else if(type.Equals("CullBackCounterClockwiseFront")) {
                    rs = RasterizerState.CullBackCounterClockwiseFront;
                    _renderStates[name] = rs;
                }
            } else {
                ThrowException("Parse error, missing syntax");
            }
        }

        private void ThrowException(String msg) {
            throw new IOException(String.Format("Error parsing material file on line {0}, token {1}. {2}", _reader.CurrentLineNumber, _reader.CurrentLineToken, msg));
        }

        private struct MaterialTechnique {
            public String Name;
            public List<MaterialPass> Passes;

            public String RasterizerStateName;
            public String BlendStateName;
            public String DepthStencilStateName;
        }

        private struct MaterialPass {
            public String Name;
            public String BlendStateName;
            public String RasterizerStateName;
            public String DepthStencilStateName;
        }
    }

    internal class TokenReader {
        private String[] _tokens;
        private int _tokenIndex;
        private StreamReader _input;
        private char[] _delims;
        private char[] _numberDelims = new char[] { 'f' };
        private bool _reachedEnd = false;
        private int _lineNum = 0;

        public int CurrentLineNumber {
            get {
                return _lineNum;
            }
        }

        public int CurrentLineToken {
            get {
                return _tokenIndex;
            }
        }

        public TokenReader(char[] tokenDelimiters) {
            _delims = tokenDelimiters;
        }

        public void Close() {
            _input.Close();
            _input = null;
            _tokenIndex = 0;
            _tokens = null;
            _reachedEnd = false;
            _lineNum = 0;
        }

        public void Read(Stream stream) {
            _input = new StreamReader(stream);
            _tokenIndex = 0;
            _tokens = null;
            _reachedEnd = false;
            _lineNum = 0;
        }

        public void SetDelimiters(char[] delims) {
            _delims = delims;
        }

        private void ReadLine() {
            while(!_input.EndOfStream) {
                String line = _input.ReadLine();
                if(!String.IsNullOrWhiteSpace(line)) {
                    _tokens = line.Trim().Split(_delims);
                    _tokenIndex = 0;
                    _lineNum++;
                    return;
                }
            }
            _reachedEnd = true;
        }

        public String NextToken() {
            if(_tokens == null || _tokenIndex >= _tokens.Length) {
                ReadLine();
            }
            while(!_reachedEnd) {
                String token = _tokens[_tokenIndex];
                if(token.StartsWith("//")) {
                    ReadLine();
                } else {
                    _tokenIndex++;
                    return token;
                }
            }
            return String.Empty;
        }

        public bool HasNext() {
            if(_tokens == null || _tokenIndex >= _tokens.Length) {
                ReadLine();
                if(_reachedEnd) {
                    return false;
                }
            }
            return true;
        }

        public bool HasNext(String token) {
            if(_tokens == null || _tokenIndex >= _tokens.Length) {
                ReadLine();
                if(_reachedEnd) {
                    return false;
                }
            }

            if(_tokens[_tokenIndex].StartsWith("//")) {
                ReadLine();
                if(_reachedEnd) {
                    return false;
                }
            }

            if(_tokens[_tokenIndex].Equals(token, StringComparison.InvariantCultureIgnoreCase)) {
                return true;
            }
            return false;
        }

        public bool HasNextStartsWith(String token) {
            if(_tokens == null || _tokenIndex >= _tokens.Length) {
                ReadLine();
                if(_reachedEnd) {
                    return false;
                }
            }

            if(_tokens[_tokenIndex].StartsWith("//")) {
                ReadLine();
                if(_reachedEnd) {
                    return false;
                }
            }

            if(_tokens[_tokenIndex].StartsWith(token, StringComparison.InvariantCultureIgnoreCase))
            {
                return true;
            }
            return false;
        }

        public float NextFloat() {
            return ParseFloat(NextToken());
        }

        public bool NextBool() {
            return ParseBool(NextToken());
        }

        public int NextInt() {
            return ParseInt(NextToken());
        }

        public float ParseFloat(String s) {
            return float.Parse(s.TrimEnd(_numberDelims), CultureInfo.InvariantCulture);
        }

        public int ParseInt(String s) {
            return int.Parse(s);
        }

        public bool ParseBool(String s) {
            return bool.Parse(s);
        }


    }*/
}
